// 《围棋》作者版权所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package escape

import (
	"cmd/compile/internal/ir"
	"cmd/compile/internal/typecheck"
)

func isSliceSelfAssign(dst, src ir.Node) bool {
	// 检测以下特殊情况。
	// 
	// func（b*Buffer）Foo（）{
	// n，m:=…
	// b.buf=b.buf[n:m]
	// }
	// 
	// 此赋值不适用于转义分析，
	// 它不会将任何尚未存在的新指针存储到b中。
	// 但是，如果没有这个特殊情况，b将逃逸，因为我们分配给OIND/ODOTPTR。
	// 这里我们假设该语句将不包含调用，也就是说，该顺序将把所有调用移到init。
	// 否则，当我们评估dst和src时，基值可能会在时刻
	// 之间发生变化。

	// dst是ONAME解引用。
	var dstX ir.Node
	switch dst.Op() {
	default:
		return false
	case ir.ODEREF:
		dst := dst.(*ir.StarExpr)
		dstX = dst.X
	case ir.ODOTPTR:
		dst := dst.(*ir.SelectorExpr)
		dstX = dst.X
	}
	if dstX.Op() != ir.ONAME {
		return false
	}
	// src是一个切片操作。好的。
	switch src.Op() {
	case ir.OSLICE, ir.OSLICE3, ir.OSLICESTR:
	case ir.OSLICEARR, ir.OSLICE3ARR:
		// 由于数组嵌入到包含对象中，
		// 非指针数组的切片将向b中引入一个新的指针，该指针不在b中
		// （指向b本身的指针）。在这样的赋值之后，如果b内容转义，
		// b也转义。如果我们忽略这样的OSLICEARR，我们将得出结论：当b内容逃逸时，b不会逃逸。
		// 
		// 指向数组的指针是可以的，因为它不是直接存储在b中的。
		// 对于切片数组（不是指向数组的指针），有一个隐式的OADDR。
		// 我们检查它以确定非指针数组切片。
		src := src.(*ir.SliceExpr)
		if src.X.Op() == ir.OADDR {
			return false
		}
	default:
		return false
	}
	// 切片应用于ONAME解引用。
	var baseX ir.Node
	switch base := src.(*ir.SliceExpr).X; base.Op() {
	default:
		return false
	case ir.ODEREF:
		base := base.(*ir.StarExpr)
		baseX = base.X
	case ir.ODOTPTR:
		base := base.(*ir.SelectorExpr)
		baseX = base.X
	}
	if baseX.Op() != ir.ONAME {
		return false
	}
	// dst和src引用同一个基址。
	return dstX.(*ir.Name) == baseX.(*ir.Name)
}

// isSelfAssign报告从src到dst的分配是否可以被转义分析忽略，因为它实际上是一个自我分配。
func isSelfAssign(dst, src ir.Node) bool {
	if isSliceSelfAssign(dst, src) {
		return true
	}

	// 检测分配回同一对象的琐碎分配。
	// 
	// 它涵盖了以下情况：
	// val.x=val.y 
	// val.x[i]=val.y[j]
	// val.x1。x2=val.x1。y2 
	// /。。。etc 
	// 
	// 这些分配不会更改分配的对象生存期。

	if dst == nil || src == nil || dst.Op() != src.Op() {
		return false
	}

	// 表达式前缀必须是“安全”且相同。
	switch dst.Op() {
	case ir.ODOT, ir.ODOTPTR:
		// 允许不同的安全跟踪访问器。
		dst := dst.(*ir.SelectorExpr)
		src := src.(*ir.SelectorExpr)
		return ir.SameSafeExpr(dst.X, src.X)
	case ir.OINDEX:
		dst := dst.(*ir.IndexExpr)
		src := src.(*ir.IndexExpr)
		if mayAffectMemory(dst.Index) || mayAffectMemory(src.Index) {
			return false
		}
		return ir.SameSafeExpr(dst.X, src.X)
	default:
		return false
	}
}

// mayAffectMemory报告n的计算是否会影响程序的
// 内存状态。如果表达式不能影响内存状态，那么它可以被逃逸分析安全地忽略。
func mayAffectMemory(n ir.Node) bool {
	// 我们可能希望使用一个“内存安全”操作列表，而不是通常的
	// “无副作用”，其中包括所有调用和其他可以分配或更改全局状态的操作。现在，从后者开始更安全。
	// 
	// 我们忽略了诸如被零除、索引超出范围、
	// 和零指针解引用之类的内容。

	// TODO（rsc）：似乎应该可以用
	// ir来代替它。任何人都在寻找案件陈述书以外的任何行动。
	// 但这会在buildall检测到的编译输出中产生更改。
	switch n.Op() {
	case ir.ONAME, ir.OLITERAL, ir.ONIL:
		return false

	case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD:
		n := n.(*ir.BinaryExpr)
		return mayAffectMemory(n.X) || mayAffectMemory(n.Y)

	case ir.OINDEX:
		n := n.(*ir.IndexExpr)
		return mayAffectMemory(n.X) || mayAffectMemory(n.Index)

	case ir.OCONVNOP, ir.OCONV:
		n := n.(*ir.ConvExpr)
		return mayAffectMemory(n.X)

	case ir.OLEN, ir.OCAP, ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
		n := n.(*ir.UnaryExpr)
		return mayAffectMemory(n.X)

	case ir.ODOT, ir.ODOTPTR:
		n := n.(*ir.SelectorExpr)
		return mayAffectMemory(n.X)

	case ir.ODEREF:
		n := n.(*ir.StarExpr)
		return mayAffectMemory(n.X)

	default:
		return true
	}
}

// HeapAllocReason返回给定节点必须为heap 
// 已分配的原因，如果未分配，则返回空字符串。
func HeapAllocReason(n ir.Node) string {
	if n == nil || n.Type() == nil {
		return ""
	}

	// 参数始终通过堆栈传递。
	if n.Op() == ir.ONAME {
		n := n.(*ir.Name)
		if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
			return ""
		}
	}

	if n.Type().Size() > ir.MaxStackVarSize {
		return "too large for stack"
	}

	if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Size() > ir.MaxImplicitStackVarSize {
		return "too large for stack"
	}

	if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() > ir.MaxImplicitStackVarSize {
		return "too large for stack"
	}
	if n.Op() == ir.OMETHVALUE && typecheck.MethodValueType(n.(*ir.SelectorExpr)).Size() > ir.MaxImplicitStackVarSize {
		return "too large for stack"
	}

	if n.Op() == ir.OMAKESLICE {
		n := n.(*ir.MakeExpr)
		r := n.Cap
		if r == nil {
			r = n.Len
		}
		if !ir.IsSmallIntConst(r) {
			return "non-constant size"
		}
		if t := n.Type(); t.Elem().Size() != 0 && ir.Int64Val(r) > ir.MaxImplicitStackVarSize/t.Elem().Size() {
			return "too large for stack"
		}
	}

	return ""
}
